﻿using System;
using System.Numerics;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Windows.Foundation;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Windows.UI;
using Microsoft.UI.Dispatching;
using Microsoft.Graphics.Canvas;
using Microsoft.UI;
using Microsoft.Graphics.Canvas.Effects;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.UI.Xaml;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Microsoft.Graphics.Canvas.Text;
using Windows.Storage;
using Windows.ApplicationModel;

// https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板

namespace FASTControlUI
{
    /// <summary>
    /// 可用于自身或导航至 Frame 内部的空白页。
    /// </summary>
    public sealed partial class HomePage : Page
    {
        internal static GameDevice gameInputDevice;
        internal static GameDevice gameOutputDevice;
        internal static GameSerialDevice serial;
        static InputReport report { get; set; }
        const ushort VID = 0x1A86;
        const ushort PID = 0xE6E1;
        struct TestData
        {
            public int Id;
            public string Name;
            public int age;
            public string address;
            public ushort A;
            public char B;
        }
        public HomePage()
        {
            this.InitializeComponent();
            if (gameInputDevice == null)
                gameInputDevice = new GameDevice(VID, PID, InputReportReceived);
            if (gameOutputDevice == null)
                gameOutputDevice = new GameDevice(VID, PID);
            if (serial == null)
                //serial = new GameSerialDevice(VID, PID);
            DataAccess.InitializeDatabase();
            DataAccessT<TestData> data = new DataAccessT<TestData>();
            //data.Init();
            //data.Add(new TestData { Id = VID, Name = "张冰", age = 26, address = "", A = 99, B = 'C' });
            //new Thread(async () => { await data.GetData(); }).Start();
            //data.DeleteData();
        }
        static public object locker = new object();
        static private float width;
        static private float heigth;
        private void InputReportReceived(InputReport args)
        {
            report = args;
            lock (locker)
            {
                if (wave.Count > width)
                    wave.Dequeue();
                wave.Enqueue(new Vector2((float)(report.Force), (float)(report.Force) / 65536 * heigth + heigth / 2));
            }
        }
        public void Invoke(Action action, DispatcherQueuePriority Priority = DispatcherQueuePriority.Normal)
        {
            this.DispatcherQueue.TryEnqueue(Priority, () => { action(); });
        }
        private bool CanvasDraw = false;
        private CanvasRenderTarget _offscreen;
        private GaussianBlurEffect _effect;

        private void Page_Unloaded(object sender, RoutedEventArgs e)
        {
            canvas.RemoveFromVisualTree();
            canvas = null;
            canvas1.RemoveFromVisualTree();
            canvas1 = null;
            canvas2.RemoveFromVisualTree();
            canvas2 = null;
            canvas3.RemoveFromVisualTree();
            canvas3 = null;
            canvas4.RemoveFromVisualTree();
            canvas4 = null;
            canvas5.RemoveFromVisualTree();
            canvas5 = null;
            CanvasDraw = false;
            MainConfig.Current.LineColor = lineColor;
        }

        private void canvas_Draw(CanvasControl sender, CanvasDrawEventArgs args)
        {
            width = (float)canvas.ActualWidth - 20;
            heigth = (float)canvas.ActualHeight - 20;
            var ds = args.DrawingSession;
            CanvasStrokeStyle strokeStyle = new CanvasStrokeStyle()
            {
                StartCap = CanvasCapStyle.Round,
                EndCap = CanvasCapStyle.Triangle,
            };
            // Draw the arc.
            using (var builder = new CanvasPathBuilder(sender))
            {
                int i;
                float textPointY;
                lock (locker)
                {
                    i = (int)sender.ActualWidth - wave.Count;
                    bezierStartPoint = wave.ElementAt(0);
                    bezierStartPoint.X = i - 1;
                    builder.BeginFigure(bezierStartPoint);
                    foreach (var point in wave)
                    {
                        var endPoint = point;
                        endPoint.X = i;
                        i++;
                        //var bezierControl = bezierStartPoint + (endPoint - bezierStartPoint) / 2;
                        //builder.AddQuadraticBezier(bezierControl, endPoint);
                        builder.AddLine(endPoint);
                        bezierStartPoint = endPoint;
                    }
                    textPointY = wave.Last().Y;
                }
                builder.EndFigure(CanvasFigureLoop.Open);
                using (var geometry = CanvasGeometry.CreatePath(builder))
                {
                    ds.DrawGeometry(geometry, lineColor, 3, strokeStyle);
                    ds.DrawText($"{(textPointY - heigth / 2) / (heigth / 2) * 100:N2}%", new Vector2(20, 0), lineColor);
                    DateTime dateTime = DateTime.Now;
                    long nowTime = dateTime.Ticks;
                    fps[len] = 10000000.0 / (nowTime - lasttime);
                    len++;
                    if (len >= 100)
                    {
                        len = 0;
                        fpss = 0;
                        for (int j = 0; j < fps.Length; j++)
                        {
                            fpss += fps[j];
                        }
                    }
                    ds.DrawText($"{fpss / 100:N2} FPS", new Vector2(100, 0), lineColor);
                    lasttime = nowTime;
                }
            }
            sender.Invalidate();
        }
        long lasttime = 0;
        int len = 0;
        double fpss = 0;
        double[] fps = new double[100];
        Vector2 bezierStartPoint = new Vector2(0, 0);
        static Queue<Vector2> wave = new Queue<Vector2>();
        Color lineColor = Colors.Blue;

        private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            wave.Enqueue(new Vector2(0, (float)(canvas.ActualHeight / 2)));
            lineColor = MainConfig.Current.LineColor;
            MainWindow.Current.SetTitleBarWhith((int)this.ActualWidth);
        }

        private void CanvasControl_Draw(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
        {

            using (var ds = args.DrawingSession)
            {
                //ds.DrawRectangle();
                float drawheigth = 0;
                float height = (float)(sender.ActualHeight * 0.95f);
                string mode = (string)sender.Tag;
                switch (mode)
                {
                    case "0":
                        drawheigth = report.Throttle / 65535.0f * height;
                        break;
                    case "1":
                        drawheigth = report.Brake / 65535.0f * height;
                        break;
                    case "2":
                        drawheigth = report.axisRx / 65535.0f * height;
                        break;
                    case "3":
                        drawheigth = report.axisRy / 65535.0f * height;
                        break;
                    default:
                        break;
                }
                Rect rect = new Rect(5, sender.ActualHeight - drawheigth, sender.ActualWidth - 10, drawheigth);
                ds.FillRoundedRectangle(rect, 20, 20, lineColor);
                CanvasTextFormat textFormat = new CanvasTextFormat();
                textFormat.HorizontalAlignment = CanvasHorizontalAlignment.Center;
                ds.DrawText($"{(drawheigth / height) * 100:N0}%", new Vector2((float)(sender.ActualWidth / 2), 0), lineColor, textFormat);
            }
            sender.Invalidate();
        }

        private void CanvasControl_Draw_1(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
        {
            using (var ds = args.DrawingSession)
            {
                float canvasWidth = (float)sender.ActualWidth;
                float canvasHeight = (float)sender.ActualHeight;
                double angle = report.axisX / 32768.0f * Math.PI * (GameDataPublic.ReportWheel.WheelDegrees / 360.0);
                Rect rect = wheelImage.GetBounds(sender);
                Transform2DEffect effect = new Transform2DEffect
                {
                    Source = wheelImage,
                    TransformMatrix = Matrix3x2.CreateRotation((float)angle, new Vector2((float)(rect.Width / 2), (float)(rect.Height / 2)))
                };
                ds.DrawImage(effect, new Vector2((float)(canvasWidth - rect.Width) / 2, (float)(canvasHeight - rect.Height) / 2));
                wheelAngleText.Angle = (float)angle;
            }
            sender.Invalidate();
        }
        public class WheelAngleText : INotifyPropertyChanged
        {
            private float angle = 0;
            public float Angle
            {
                get { return angle; }
                set
                {
                    angle = (float)(value / Math.PI * 180);
                    OnPropertyChanged();
                }
            }
            public String getText
            {
                get { return $"位置：{this.angle:N2}°"; }
            }
            public void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                // Raise the PropertyChanged event, passing the name of the property whose value has changed.
                this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("getText"));
            }
            public event PropertyChangedEventHandler PropertyChanged;
        }
        WheelAngleText wheelAngleText = new WheelAngleText();
        ICanvasImage wheelImage;
        private void CanvasControl_CreateResources(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender, Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
        {
            Task task = this.CreateResourcesAsync(sender);
            IAsyncAction action = task.AsAsyncAction();
            args.TrackAsyncAction(action);
        }
        private async Task CreateResourcesAsync(CanvasControl sender)
        {
            string path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\FASTControlUI\\Assets";
            var buffer = Path.Combine(path, "wheel_blue.png");
            CanvasBitmap canvasBitmap;
            if (File.Exists(buffer))
            {
                canvasBitmap = await CanvasBitmap.LoadAsync(sender, buffer);
            }
            else
            {
                if (!Directory.Exists(path))
                    Directory.CreateDirectory(path);
                if (Path.GetFullPath("./").Equals("C:\\Windows\\System32\\", StringComparison.OrdinalIgnoreCase))
                {
                    StorageFolder storageFolder = Package.Current.InstalledLocation;
                    StorageFile storageFile = await storageFolder.GetFileAsync("Assets\\wheel_blue.png");
                    File.Copy(storageFile.Path, buffer);
                }
                else
                {
                    File.Copy(Path.GetFullPath("./Assets/wheel_blue.png"), buffer);
                }
                if (!File.Exists(buffer))
                    canvasBitmap = CanvasBitmap.CreateFromColors(sender, new Color[16], 2, 2);
                else
                    canvasBitmap = await CanvasBitmap.LoadAsync(sender, buffer);
            }
            Color[] colors = canvasBitmap.GetPixelColors();
            for (int i = 0; i < colors.Length; i++)
            {
                if (colors[i].A != 0)
                {
                    colors[i] = lineColor;
                }
            }
            canvasBitmap.SetPixelColors(colors);
            wheelImage = new Transform2DEffect
            {
                Source = canvasBitmap,
                TransformMatrix = Matrix3x2.CreateScale((float)(250 / canvasBitmap.Size.Width))
            };
        }
    }
}
